package com.practice.zuocy.base.class08;

import java.util.LinkedList;

/**
 * 【题目】：
 * 给定一颗二叉树的头节点head，返回这棵树是不是完全二叉树
 */
public class Code06_IsCBT {

	public static class Node {
		public int value;
		public Node left;
		public Node right;

		public Node(int data) {
			this.value = data;
		}
	}

	/**
	 * 层序遍历的方式求解
	 * @param head
	 * @return
	 */
	public static boolean isCBT1(Node head) {
		if (head == null) {
			return true;
		}
		LinkedList<Node> queue = new LinkedList<>();
		// 是否遇到过左右两个孩子不双全的节点
		boolean leaf = false;
		Node l = null;
		Node r = null;
		queue.add(head);
		while (!queue.isEmpty()) {
			head = queue.poll();
			l = head.left;
			r = head.right;
			if (
			// 1. 如果遇到了不双全的节点之后，又发现当前节点不是叶节点
			(leaf && (l != null || r != null))
					||
			// 2. 有右无左
			(l == null && r != null)

			) {
				return false;
			}
			if (l != null) {
				queue.add(l);
			}
			if (r != null) {
				queue.add(r);
			}
			if (l == null || r == null) {
				// 发现左右两个孩子不双全
				leaf = true;
			}
		}
		return true;
	}

	public static boolean isCBT2(Node head) {
		if (head == null) {
			return true;
		}
		return process(head).isCBT;
	}

	// 对每一棵子树，
	// （1）是否是满二叉树、
	// （2）是否是完全二叉树、
	// （3）高度
	public static class Info {
		// 当前子树是不是满的
		public boolean isFull;
		// 当前子树是不是完全二叉树
		public boolean isCBT;
		// 当前子树的高度
		public int height;

		public Info(boolean full, boolean cbt, int h) {
			isFull = full;
			isCBT = cbt;
			height = h;
		}
	}

	public static Info process(Node X) {

		/*
		 * 根据最后一层，最后一个节点到哪了作为标准，进行分类：
		 * （1）满二叉树（无缺口）
		 * （2）有缺口：
		 *     （2.1）缺口位置在左树上
		 *         （2.1.1）填满的左树
		 *         （2.1.2）没有填满左树
		 *     （2.2）缺口位置在右树上
		 *         （2.2.1）没有填满右树
		 * 以上几种情况是完全二叉树
		 */

		if (X == null) {
			return new Info(true, true, 0);
		}
		// 左树信息
		Info leftInfo = process(X.left);
		// 右树信息
		Info rightInfo = process(X.right);

		// 当前子树的高度为 左树高度和右树高度的最大值+1
		int height = Math.max(leftInfo.height, rightInfo.height) + 1;

		// 当前是否是满的：
		// （1）左树是满的；
		// （2）右树是满的；
		// （3）左树高度等于右树高度
		boolean isFull = leftInfo.isFull 
				&& 
				rightInfo.isFull 
				&& leftInfo.height == rightInfo.height;
		
		// 当前子树是否是完全二叉树
		boolean isCBT = false;
		if (isFull) {
			// （1）如果当前子树是满的，则当前子树一定是完全二叉树
			isCBT = true;
		} else { // 以x为头整棵树，不满
			if (leftInfo.isCBT && rightInfo.isCBT) {// 左树是完全二叉树，并且，右树是完全二叉树
				if (
						// 左树是完全二叉树
						leftInfo.isCBT
						&&
						// 右树是满的
						rightInfo.isFull
						&&
						// 左树高度比右树高度大1
						leftInfo.height == rightInfo.height + 1
				) {
					isCBT = true;
				}
				if (
						// 左树是满的
						leftInfo.isFull
						&&
						// 右树是满的
						rightInfo.isFull 
						&&
						// 左树的高度比右树的高度大1
						leftInfo.height == rightInfo.height + 1) {
					isCBT = true;
				}
				if (
						// 左树是满的
						leftInfo.isFull
						&&
						// 右树是完全二叉树
						rightInfo.isCBT
						&&
						// 左树高度等于右树高度
						leftInfo.height == rightInfo.height) {
					isCBT = true;
				}
				
				
			}
		}
		// 返回当前子树的信息
		return new Info(isFull, isCBT, height);
	}

	// for test
	public static Node generateRandomBST(int maxLevel, int maxValue) {
		return generate(1, maxLevel, maxValue);
	}

	// for test
	public static Node generate(int level, int maxLevel, int maxValue) {
		if (level > maxLevel || Math.random() < 0.5) {
			return null;
		}
		Node head = new Node((int) (Math.random() * maxValue));
		head.left = generate(level + 1, maxLevel, maxValue);
		head.right = generate(level + 1, maxLevel, maxValue);
		return head;
	}

	public static void main(String[] args) {
		int maxLevel = 5;
		int maxValue = 100;
		int testTimes = 1000000;
		for (int i = 0; i < testTimes; i++) {
			Node head = generateRandomBST(maxLevel, maxValue);
			if (isCBT1(head) != isCBT2(head)) {
				System.out.println("Oops!");
			}
		}
		System.out.println("finish!");
	}

}
